import {
  AfterContentInit, AfterViewInit, ChangeDetectorRef, Component, ContentChild, ContentChildren, ElementRef, EventEmitter, Input,
  OnChanges, OnDestroy, OnInit, Output, QueryList,
  SimpleChanges, TemplateRef, ViewChild
} from '@angular/core';
import { NzContextMenuService, NzDropdownMenuComponent } from 'ng-zorro-antd/dropdown';
import { NzTableComponent } from 'ng-zorro-antd/table';
import { Base } from '../../core/view/base';
import { isDate, isNumber, pick } from '../../util';
import { TableColumnComponent } from './table-column.component';
import { TableSelectionComponent } from './table-selection.component';
import { TableSortingComponent } from './table-sorting.component';

export type Align = 'left' | 'right' | 'center';
export type Position = Align | 'all' | 'leftAndRight';
// bzSummary
let tableKey = 0;
@Component({
  selector: 'bz-table',
  templateUrl: './table.component.html',
  styles: []
})
export class TableComponent<U, B, S> extends Base<U, B, S> implements OnInit, OnChanges, AfterViewInit, AfterContentInit, OnDestroy {
  @ContentChildren(TableColumnComponent) tableColumnComponent: QueryList<TableColumnComponent>;
  @ContentChild(TableSortingComponent, { static: false }) tableSortingComponent: TableSortingComponent;
  @ContentChild(TableSelectionComponent, { static: false }) tableSelectionComponent: TableSelectionComponent;
  @ViewChild('bzTable', { static: false }) nzTableComponent: NzTableComponent;

  /** 表格名称-用于表格自定义列标识 */
  @Input() name: string;
  /** 数据数组 */
  @Input() bzDataSource: any[] = [];
  /** 是否显示分页器 */
  @Input() bzShowPagination = true;
  /** 是否在前端对数据进行分页，如果在服务器分页数据或者需要在前端显示全部数据时传入 false */
  @Input() bzFrontPagination = true;
  /** 当前页码，可双向绑定 */
  @Input() bzPageIndex = 1;
  /** 每页展示多少数据，可双向绑定 */
  @Input() bzPageSize = 20;
  /** 页数选择器可选值 */
  @Input() bzPageSizeOptions: number[] = [20, 50, 100];
  /** 是否可以快速跳转至某页 */
  @Input() bzShowQuickJumper = true;
  /** 页面是否加载中 */
  @Input() bzLoading = false;
  /** 当为「small」时，是小尺寸分页 */
  @Input() bzSize: 'small' | 'default' = 'default';
  /** 当添加该属性时，显示为简单分页 */
  @Input() bzSimple: boolean;
  /** trackByKey */
  @Input() bzTrackByKey = 'index';
  /** 用于显示数据总量和当前数据范围，用法参照 Pagination 组件 */
  @Input() bzShowTotalTpl: TemplateRef<{ $implicit: number, range: [number, number] }>;
  /** 无数据时显示内容 */
  @Input() bzNoResultTpl: string | TemplateRef<void>;
  /** 嵌套子表格，用 expand 控制显示与否*/
  @Input() bzMasterDetail: TemplateRef<any>;
  /** 表格宽度-表格宽度的固定值或百分比 */
  @Input() bzWidth = '100%';
  /** 表格高度-表格高度的固定值 */
  @Input() bzHeight: string;
  /** 选择模式 */
  @Input() bzSelectionMode: 'multiple' | 'none' | 'single' = 'none';
  /** 选择项key */
  @Input() bzKeyExpr: string;
  /** 选择项-可双向绑定 */
  @Input() bzSelectedRowKeys: any[];
  @Output() bzSelectedRowKeysChange = new EventEmitter<Array<any>>();

  /** 当前选择项改变时的回调函数 */
  @Output() bzOnSelectionChanged = new EventEmitter<Array<any>>();
  /** 当前页码改变时的回调函数 */
  @Output() bzSortChange = new EventEmitter<any>();
  /** 当前页码改变时的回调函数 */
  @Output() bzPageIndexChange = new EventEmitter<number>();
  /** 页数改变时的回调函数 */
  @Output() bzPageSizeChange = new EventEmitter<number>();
  /** 当前页面展示数据改变的回调函数 */
  @Output() bzCurrentPageDataChange = new EventEmitter<Array<any>>();

  /** 当前总数据，在服务器渲染时需要传入 */
  bzTotal = 1;
  /** 服务端查询无数据 */
  asyncNoResult = false;

  element: Element;
  height: string;
  paddingWidth = 14;
  // 除内容以外的padding、排序图标的宽度和
  otherWidth = this.paddingWidth + 19;
  listOfData: any[] = [];
  localTableKey;
  columns: TableColumnComponent[] = [];
  leftColumns: TableColumnComponent[] = [];
  centerColumns: TableColumnComponent[] = [];
  rightColumns: TableColumnComponent[] = [];
  activeColumn: TableColumnComponent;
  listOfSelection: Array<{ text: string, onSelect: any }> = [];
  isAllDisplayDataChecked = false;
  isIndeterminate = false;

  private listOfDataDefault: any[] = [];

  constructor(private ele: ElementRef, private changeDetectorRef: ChangeDetectorRef, private nzContextMenuService: NzContextMenuService) {
    super();
    this.element = this.ele.nativeElement;
  }

  /** 更新选择状态 */
  selectStatus(): void {
    this.isAllDisplayDataChecked = this.listOfData.every(item => item.checked);
    this.isIndeterminate = this.listOfData.some(item => item.checked) && !this.isAllDisplayDataChecked;
    this.selectChange(this.listOfData);
  }
  /** 关闭所有展开项 */
  closeAllExpand() {
    this.listOfData.forEach(item => item.expand = false);
  }

  setBzTotal(total) {
    this.bzTotal = total;
  }
  setBzFrontPagination(frontPagination) {
    this.bzFrontPagination = frontPagination;
    this.bzTotal = this.bzFrontPagination ? this.bzTotal : 1;
    this.changeDetectorRef.detectChanges();
  }

  updateColumns() {
    this.columns = this.tableColumnComponent.filter((item) => item.visible === true);
    this.leftColumns = this.columns.filter(item => item.fixed === true && item.fixedPosition === 'left').sort((a, b) => a.id - b.id);
    this.centerColumns = this.columns.filter(item => !item.fixed || !item.fixedPosition).sort((a, b) => a.id - b.id);
    this.rightColumns = this.columns.filter(item => item.fixed === true && item.fixedPosition === 'right').sort((a, b) => a.id - b.id);
    this.changeDetectorRef.detectChanges();
    this.ngZone.runOutsideAngular((e) => {
      setTimeout(() => {
        this.getTableHeight();
        this.computerWidth(true, 'all');
      }, 16);
    });
  }
  ngOnInit(): void {
    tableKey++;
    this.localTableKey = 'bztable' + tableKey;
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.bzDataSource && changes.bzDataSource.currentValue && Array.isArray(changes.bzDataSource.currentValue) && changes.bzDataSource.currentValue.length > 0) {
      this.initDataSource([...changes.bzDataSource.currentValue]);
    }
    if (changes.bzSelectedRowKeys && changes.bzSelectedRowKeys.currentValue && Array.isArray(changes.bzSelectedRowKeys.currentValue) && changes.bzSelectedRowKeys.currentValue.length > 0) {
      this.selected([...changes.bzSelectedRowKeys.currentValue]);
    }
  }

  ngAfterViewInit(): void {
    this.updateColumns();
  }

  ngAfterContentInit(): void {
    if (this.tableSelectionComponent) {
      this.tableSelectionComponent.propertyChanges.subscribe(e => {
        if (e.type === 'mode' || e.type === 'showCheckBoxesMode') {
          this.computerWidth(true, 'left');
        }
      });
    }
    if (this.tableSelectionComponent) {
      if (Array.isArray(this.tableSelectionComponent.bzSelections) && this.tableSelectionComponent.bzSelections.length > 0) {
        this.listOfSelection = this.tableSelectionComponent.bzSelections;
      }
    }
  }

  ngOnDestroy(): void {

  }

  contextFixedMenu($event: MouseEvent, menu: NzDropdownMenuComponent, column: TableColumnComponent): void {
    this.activeColumn = column;
    this.nzContextMenuService.create($event, menu);
  }
  checkItem(value: boolean, index): void {
    if ((this.tableSelectionComponent && this.tableSelectionComponent.mode === 'multiple') || this.bzSelectionMode === 'multiple') {
      this.listOfData[index].checked = value;
      this.selectStatus();
    } else if ((this.tableSelectionComponent && this.tableSelectionComponent.mode === 'single') || this.bzSelectionMode === 'single') {
      this.cancerCheckAll();
      this.listOfData[index].checked = true;
      this.selectChange([this.listOfData[index]]);
    }
  }
  checkItemTr(e, data: any, index: number): void {
    if (e.target.className.includes('bzDrop-td') || e.target.className.includes('unex-icon-drag')) {
      return;
    }
    if (e.target.className.includes('ant-table-row-expand-icon-cell')) {
      return;
    }
    if (data.disabled) {
      return;
    }
    if (this.tableSelectionComponent && this.tableSelectionComponent.showCheckBoxesMode === 'onClick') {
      data.checked = !data.checked;
      this.checkItem(data.checked, index);
    }
  }
  cancerCheckAll(): void {
    for (let i = 0; i < this.listOfData.length; i++) {
      this.listOfData[i].checked = false;
    }
  }
  checkPage(value: boolean): void {
    const selectList = this.listOfData.slice(this.bzPageIndex - 1, this.bzPageIndex * this.bzPageSize).filter(item => !item.disabled);
    this.selected(selectList, value);
    this.selectStatus();
  }
  checkAll(value: boolean): void {
    for (let i = 0; i < this.listOfData.length; i++) {
      if (this.listOfData[i].disabled) {
        continue;
      }
      this.listOfData[i].checked = value;
    }
    this.selectStatus();
  }
  trTrackByFn(index, item) {
    return item[this.bzTrackByKey];
  }
  tdTrackByFn(index, item) {
    return item['dataField'];
  }
  textFn(column: TableColumnComponent, data: object): any {
    let value = data[column.dataField];
    const dataFields = column.dataField.split('.');
    if (dataFields.length > 0) {
      let valueObj = data;
      for (let i = 0; i < dataFields.length; i++) {
        valueObj = valueObj[dataFields[i]];
      }
      value = valueObj;
    }
    const _value = column.customizeText ? column.customizeText(({ value: value })) : value;
    return _value;
  }
  alignFn(column: TableColumnComponent, data: object): Align {
    let align: Align = 'left';
    if (column.alignment) {
      return column.alignment;
    }
    if (this.listOfData.length > 0) {
      const value = this.textFn(column, data);

      if (value === null || value === '') {
        return 'left';
      }
      if (isNumber(value)) {
        align = 'right';
      }
      if (isDate(value)) {
        align = 'left';
      }
    }
    return align;
  }
  getShowSelect(): boolean {
    return (this.tableSelectionComponent && (this.tableSelectionComponent.mode === 'multiple' || this.tableSelectionComponent.mode === 'single') && this.tableSelectionComponent.showCheckBoxesMode !== 'none')
      || (this.bzSelectionMode === 'multiple' || this.bzSelectionMode === 'single');
  }
  showSortFn(column: TableColumnComponent): boolean {
    if (this.tableSortingComponent) {
      if (column.allowSorting === undefined) {
        return true;
      }
    }
    return column.allowSorting;
  }
  getColSpan(): number {
    let span = this.columns.length;
    // 嵌套表格
    if (this.bzMasterDetail) {
      span += 1;
    }
    // 单选框或多选框
    if (this.getShowSelect()) {
      span += 1;
    }
    // 自定义列功能
    if (this.name) {
      span += 1;
    }
    return span;
  }
  sortChange(e: { key: string, value: string }): void {
    if (this.bzFrontPagination) {
      if (e.value !== null) {
        const list = [...this.listOfData];
        this.listOfData = list.sort((a, b) => e.value === 'ascend' ? a[e.key] > b[e.key] ? 1 : -1 : b[e.key] > a[e.key] ? 1 : -1);
      } else {
        this.listOfData = [];
        this.listOfData = [...this.listOfDataDefault];
      }
    }
    this.bzSortChange.emit(e);
  }
  fixedChange(position: 'left' | 'right' | 'center', column: TableColumnComponent, disabled): void {
    if (disabled) {
      return;
    }
    const active = (column.fixed === true && column.fixedPosition === 'left') ? 'left' :
      (column.fixed === true && column.fixedPosition === 'right') ? 'right' : 'center';
    if (active === 'center') {
      this.centerColumns.splice(this.centerColumns.findIndex((item) => item.id === column.id), 1);
    }
    if (active === 'left') {
      this.leftColumns.splice(this.leftColumns.findIndex((item) => item.id === column.id), 1);
    }
    if (active === 'right') {
      this.rightColumns.splice(this.rightColumns.findIndex((item) => item.id === column.id), 1);
    }

    if (position === 'left') {
      column.fixed = true;
      column.fixedPosition = 'left';
      this.leftColumns.push(column);
    } else if (position === 'right') {
      column.fixed = true;
      column.fixedPosition = 'right';
      this.rightColumns.unshift(column);
    } else {
      column.fixed = false;
      column.fixedPosition = 'left';
      if (active === 'left') {
        this.centerColumns.unshift(column);
      } else if (active === 'right') {
        this.centerColumns.push(column);
      }
      this.centerColumns = this.centerColumns.sort((a, b) => a.id - b.id);
    }

    this.ngZone.runOutsideAngular(() => {
      setTimeout(() => {
        if (active === 'center') {
          this.computerWidth(false, position);
        } else if ((active === 'left' || active === 'right') && position === 'center') {
          this.computerWidth(false, active);
        } else {
          this.computerWidth(false, 'leftAndRight');
        }
      }, 16);
    });
    this.nzContextMenuService.close();
  }

  private computerWidth(recapture = true, position: Position): void {
    const tr = document.querySelector(`#${this.localTableKey}tr`);
    const th_left = tr.querySelectorAll('.bz-table-th-left');
    const th_center = tr.querySelectorAll('.bz-table-th-center');
    const th_right = tr.querySelectorAll('.bz-table-th-right');

    if (position === 'left' || position === 'leftAndRight' || position === 'all') {
      for (let index = 0; index < th_left.length; index++) {
        if (recapture) {
          const item = th_left[index];
          const custom_width = this.leftColumns[index].width === undefined
            ? undefined : (this.leftColumns[index].width) > 0
              ? (this.leftColumns[index].width) : 60;
          const width = custom_width === undefined ? this.getComputedWidth(item) : custom_width;
          this.leftColumns[index].nzWidth = width;
          // console.log('left', width);
        }
        const left = this.getFixedLeftWidth(index);
        // console.log('%c%s', 'background-color:#000;color:#fff;', `第${index}个nzLeft：`, left);
        this.leftColumns[index].nzLeft = left;
      }
    }
    if (position === 'center' || position === 'all') {
      for (let index = 0; index < th_center.length; index++) {
        if (recapture) {
          const item = th_center[index];
          const custom_width = this.centerColumns[index].width === undefined
            ? undefined : (this.centerColumns[index].width) > 0
              ? (this.centerColumns[index].width) : 60;
          const width = custom_width === undefined ? this.getComputedWidth(item) : custom_width;
          this.centerColumns[index].nzWidth = width;
          // console.log('center', width);
        }
      }
    }
    if (position === 'right' || position === 'leftAndRight' || position === 'all') {
      for (let index = th_right.length - 1; index >= 0; index--) {
        if (recapture) {
          const item = th_right[index];
          const custom_width = this.rightColumns[index].width === undefined
            ? undefined : (this.rightColumns[index].width) > 0
              ? (this.rightColumns[index].width) : 60;
          const width = custom_width === undefined ? this.getComputedWidth(item) : custom_width;
          this.rightColumns[index].nzWidth = width;
          // console.log('right', width);
        }
        const right = this.getFixedRightWidth(index);
        // console.log('%c%s', 'background-color:#000;color:#fff;', `第${index}个nzRight：`, right);
        this.rightColumns[index].nzRight = right;
      }
    }
    this.nzTableComponent.tableBodyNativeElement.focus();
    this.changeDetectorRef.detectChanges();
  }
  private getComputedWidth(item: Element) {
    const content_Ele = item.querySelector('.bz-table-th');
    const font_size = window.getComputedStyle(content_Ele).getPropertyValue('font-size');
    const font_family = window.getComputedStyle(content_Ele).getPropertyValue('font-family');
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    context.font = `${font_size} ${font_family}`;

    const metrics: TextMetrics = context.measureText(content_Ele.textContent.trim());
    return Math.ceil(metrics.width) * 1.5 + this.otherWidth;
  }
  private getTableHeight() {
    const paginationEle = document.querySelector(`#${this.localTableKey} #${this.localTableKey}pagination`);
    const tableHeader = document.querySelector(`#${this.localTableKey} #${this.localTableKey}tr`);
    const paginationHeight = parseInt(window.getComputedStyle(paginationEle).getPropertyValue('height'), 10);
    const tableHeaderHeight = parseInt(window.getComputedStyle(tableHeader).getPropertyValue('height'), 10);
    if (this.bzHeight) {
      this.height = (parseInt(this.bzHeight, 10) - paginationHeight - tableHeaderHeight) + 'px';
    }
  }
  private getFixedLeftWidth(size: number): number {
    let width = 0;
    // 嵌套表格
    if (this.bzMasterDetail) {
      width += 40;
    }
    // 单选框或多选框
    if (this.getShowSelect()) {
      width += 60;
    }
    if (size === 0) {
      return width;
    }
    for (let i = 0; i < size; i++) {
      const widthNumber = this.leftColumns[i].nzWidth;
      width = +width + +widthNumber;
    }
    return width;
  }
  private getFixedRightWidth(size: number): number {
    size = this.rightColumns.length - size;
    let width = 0;
    // 自定义列功能
    if (this.name) {
      width += 39;
    }
    if (size === 1) {
      return width;
    }
    for (let i = 1; i < size; i++) {
      const widthNumber = this.rightColumns[this.rightColumns.length - i].nzWidth;
      width = +width + +widthNumber;
    }
    return width;
  }
  private selectChange(list: any[]): void {
    const _list = list.filter(item => item.checked);
    for (let i = 0; i < _list.length; i++) {
      if (this.bzKeyExpr) {
        _list[i] = _list[i][this.bzKeyExpr];
      } else {
        _list[i] = pick(_list[i], '-checked -expand **');
      }
    }
    this.bzOnSelectionChanged.emit(_list);
    this.bzSelectedRowKeysChange.emit(_list);
  }
  private initDataSource(dataSource: any[] = []) {
    this.listOfDataDefault = this.listOfData = dataSource;
    this.bzTotal = this.listOfData.length;
  }
  private selected(selectList: any[] = [], checked: boolean = true) {
    if (this.listOfData && this.listOfData.length > 0 && selectList.length > 0) {
      const dataSource = this.listOfData;
      for (let i = 0; i < dataSource.length; i++) {
        const element = dataSource[i];
        if (this.bzKeyExpr) {
          dataSource[i].checked = (checked && selectList.find(item => item === element[this.bzKeyExpr])) ? true : false;
        } else {
          const _elementString = JSON.stringify(pick(element, '-expand -checked **'));
          dataSource[i].checked = (checked && selectList.find(item => {
            const itemString = JSON.stringify(pick(item, '-expand -checked **'));
            return itemString === _elementString;
          })) ? true : false;
        }
      }
      this.listOfDataDefault = this.listOfData = dataSource;
    }
  }
}
